local BasePlugin = require "kong.plugins.base_plugin"
local timestamp = require "kong.tools.timestamp"
--local access = require "src.access"
local kong = kong
local error = error
local table = table
local type = type
local string_find = string.find
local ACCESS_TOKEN = "access_token"

local OauthTokenHandler = BasePlugin:extend()

OauthTokenHandler.PRIORITY = 2000

local function load_token(access_token)
  local credentials, err =
  kong.db.oauth2_tokens:select_by_access_token(access_token)

  if err then
    return nil, err
  end

  if not credentials then
    return
  end

  return credentials
end

local function get_db_token(access_token)
  local token, err

  if access_token then
    local token_cache_key = kong.db.oauth2_tokens:cache_key(access_token)
    token, err = kong.cache:get(token_cache_key, nil,
            load_token,
            access_token)
    if err then
      return error(err)
    end
  end

  return token
end

local function token_renewal(token)
  kong.log('id:',token.id)
  kong.db.oauth2_tokens:update({
    id = token.id
  }, {
    expires_in = token.expires_in + 300
  })
end

local function retrieve_parameters()
  -- OAuth2 parameters could be in both the querystring or body
  local uri_args = kong.request.get_query()
  local method   = kong.request.get_method()

  if method == "POST" or method == "PUT" or method == "PATCH" then
    local body_args = kong.request.get_body()

    return kong.table.merge(uri_args, body_args)
  end

  return uri_args
end

local function parse_access_token(conf)
  local found_in = {}

  local access_token = kong.request.get_header(conf.auth_header_name)
  if access_token then
    local parts = {}
    for v in access_token:gmatch("%S+") do -- Split by space
      table.insert(parts, v)
    end

    if #parts == 2 and (parts[1]:lower() == "token" or
            parts[1]:lower() == "bearer") then
      access_token = parts[2]
      found_in.authorization_header = true
    end

  else
    access_token = retrieve_parameters()[ACCESS_TOKEN]
    if type(access_token) ~= "string" then
      return
    end
  end

  if conf.hide_credentials then
    if found_in.authorization_header then
      kong.service.request.clear_header(conf.auth_header_name)

    else
      -- Remove from querystring
      local parameters = kong.request.get_query()
      parameters[ACCESS_TOKEN] = nil
      kong.service.request.set_query(parameters)

      local content_type = kong.request.get_header("content-type")
      local is_form_post = content_type and
              string_find(content_type, "application/x-www-form-urlencoded", 1, true)

      if kong.request.get_method() ~= "GET" and is_form_post then
        -- Remove from body
        parameters = kong.request.get_body() or {}
        parameters[ACCESS_TOKEN] = nil
        kong.service.request.set_body(parameters)
      end
    end
  end

  return access_token
end

function OauthTokenHandler:new()
  OauthTokenHandler.super.new(self, "kong-oauth2")
end

-- Since this plugin will run before request is proxied to the upstream server,
-- our access function will have to take care of authentication/authorization for the request
function OauthTokenHandler:access(conf)
  OauthTokenHandler.super.access(self)

  local access_token = parse_access_token(conf)
  if not access_token then
    return
  end

  local token = get_db_token(access_token)
  if not token then
    return
  end

  if token.expires_in > 0 then -- zero means the token never expires
    local now = timestamp.get_utc() / 1000
    local diffTime = now - token.created_at
    if diffTime > token.expires_in-300 and diffTime < token.expires_in then
      token_renewal(token)
    end
  end

  return

end

return OauthTokenHandler
